#ifndef CAMERA_H
#define CAMERA_H

#include "bga/math/Quaternion.h"
#include "bga/math/Matrix.h"
#include <string>


/**
 * \namespace bga
 * \brief API namespace
 */
namespace bga
{

namespace CameraDirection
{

    /**
     * \brief set of directions that the camera can take
     */
    enum Direction
    {
        UP,
        DOWN,
        LEFT,
        RIGHT,
        FORWARD,
        BACKWARD
    };

}

namespace CameraType
{
    /**
     * \brief define the different type of camera
     */
    enum Type
    {
        FREEFLY,
        TRACKBALL
    };
}

/**
 * \brief defines a simple Camera.
 *
 * Defines a simple Camera. This class is abstract
 */
class BOARD_HELL_ENGINE Camera
{
    protected:
        Quaternion direction; ///!< Center of look of the camera
        Quaternion position;  ///!< Position of the camera

        double fov;       ///!< Vertical angle
        double angle;     ///!< Horizontal angle
        double left;      ///!< Left plan
        double right;     ///!< Right plan
        double near;      ///!< Near plan
        double far;       ///!< Far plan
        double top;       ///!< Top plan
        double bottom;    ///!< Bottom plan

        //float mat_modelView[16];    ///!< ModelView matrix
        //float mat_pos[16];          ///!< Position matrix
        //float mat_projection[16];   ///!< Projection Matrix

        Matrix4f modelViewMatrix;   ///!< ModelView matrix
        Matrix4f positionMatrix;         ///!< Position matrix
        Matrix4f projectionMatrix;  ///!< Projection matrix

        CameraType::Type type;    ///!< Type of the camera

    public:
        /**
         * \brief Constructor
         * \param position : intiial position of the camera
         */
        Camera(const Vector3d& position, CameraType::Type type);


        /**
         * Destructor
         */
        virtual ~Camera();

        /**
         * \brief returns a textual representation of this object
         * \return textual representation of this object
         */
        virtual std::string toString() const;

        /**
         * \brief Set the position of the camera
         * \param position : new position of the camera
         */
        void setPosition(const Quaternion& position);

        /**
         * \brief Get the center of look of the camera
         * \return the center of look of the camera
         */
        const Quaternion& getDirection() const;

        /**
         * \brief Set the center of look of the camera
         * \param direction : the center of look of the camera
         */
        void setDirection(const Quaternion& direction);

        /**
         * \brief get the type of the camera
         * \return the type of the camera
         */
        CameraType::Type getType() const;

        virtual void onMouseMoved(int dmx, int dmz) = 0;
        virtual void onKeyDown(CameraDirection::Direction key) = 0;

        /**
         * \brief to display the scene
         */
        virtual void look();

        /**
         * \brief compute the final transformation matrix of a scene
         */
        virtual void buildTransformationMatrix() = 0;

        /**
         * \brief compute the final projection matrix of a scene
         */
        void buildProjectionMatrix();

        /**
         * \brief compute and set the final projection matrix of a scene
         */
        void glUpdateProjectionMatrix();

        /**
         * \brief set the vertical angle and update the projection matrix
         * \param value : new value of the vertical angle
         */
        void setAspectRatio( double value );

        /**
         * \brief set the near and far and update the projection matrix
         * \param near_plane : new value of the near plan
         * \param far_plane : new value of the far plan
         */
        void setPlanes( double near_plane, double far_plane );

        /**
         * \brief set the horizontal angle and update the projection matrix
         * \param value : new value of the horizontal angle
         */
        void setFOV( double angle );

        /**
         * \brief get the ModelView matrix
         * \return ModelView Matrix
         */
        //const float* getViewMatrix() const;
        Matrix4f* getViewMatrix();

        /**
         * \brief get the Projection matrix
         * \return Projection Matrix
         */
        //const float* getProjectionMatrix() const;
        Matrix4f* getProjectionMatrix();

        /**
         * \brief used to know the orientation for picking... considering camera position, orientation and mouse position
         * \param orientation : current oriantation of the camera
         * \param x : x coordinate of the mouse position
         * \param y : y coordinate of the mouse position
         * \param near : near plan
         * \return the orientation from position to mouse position
         *
         * representing mouse position      sample: (0,0) represent the mouse located in the center.
         */
        virtual Vector3d getMouseOrientation(double x, double y)=0;

        virtual Vector3d getOrientation() const = 0;

        virtual Vector3d getPosition() const =0;


    protected:
        /**
         * \brief Init the quaternion representing the direction
         * \param position : position of the camera
         * \param center : center of look of the camera
         * \param up : up vector of the camera
         *
         * This funtion init the quaternion representing the direction. To do that, it will compute
         * the same initial matrix than gluLookAt called with the same parameters
         */
        void initializeQuaternionDirection(const Vector3d& position, const Vector3d& center, const Vector3d& up);



};

}

#endif // CAMERA_H
